#include "shell.h"
#include "cli.h"
#include "sh_mem.h"
#include "os_io.h"
#include "err.h"
#include "string.h"
#include "flash_controller.h"

// DUMMY DCLS

err_t cli_profiler(uint8_t *command, bool *exited);
uint8_t * cli_profiler_mode(void);

// DUMMY DCLS END


static cli_clients_t current_client = CLI_CLIENT_SYSTEM;

static client_info_t cli_clients[CLI_CLIENT_MAX] = 
   { 
     { "", NULL, NULL },
     { "profiler", cli_profiler, cli_profiler_mode }
   };

static cmd_table_t cmd_table[CLI_SYSTEM_CMDS_MAX] = 
 {
   { "help", invoke_help },
   { "flashdump", spi_flash_dump }, 
   { "reset", do_reset }
 };

// DUMMY FUNCTIONS
err_t cli_profiler(uint8_t *command, bool *exited) {
    printf("Called profiler with command: %s\n", command);
    *exited = true;
    return ERR_SUCCESS;
}

uint8_t *cli_profiler_mode(void)
{
  return (uint8_t *)"PROFILER";
}

void spi_flash_dump(uint8_t *command)
{
    dump_flash_regs();
    return;
}

void invoke_help(uint8_t *command)
{
  uint16_t i = 0;
  printf("System Commands:\n");

  for (i = 0; i < CLI_SYSTEM_CMDS_MAX; i++) {
      printf("%s\n", cmd_table[i].command);
  }

  printf("\nInstalled CLI:\n");
  for (i = 1; i < CLI_CLIENT_MAX; i++) {
      printf("%s\n", cli_clients[i].cli_name);
  }
  
}

void do_reset(uint8_t *command)
{
  printf("called do_reset\n");
}

// DUMMY FUNCTIONS END




void start_shell(cli_cmd_info_t *cmd_ptr)
{
    err_t err_code;
    bool exited;
    uint8_t i = 0;

    while (1) {
        // block on sem
	err_code = sem_lock(&cmd_ptr->cli_sem);
	if (err_code != ERR_SUCCESS) {
	    printf("Error locking the cli sem. shell process exiting\n");
	    return;
	}

	// Return early if just an enter key was pressed
	if (cmd_ptr->cmd_buffer[0] == 0) {
	    printf("%s", cmd_ptr->prompt);
	    cmd_ptr->client_busy = FALSE;
	    continue;
	}

	// process command
	if (current_client != CLI_CLIENT_SYSTEM) {
	    cli_clients[current_client].cli_func(cmd_ptr->cmd_buffer, &exited);
	    if (exited) {
	        current_client = CLI_CLIENT_SYSTEM;
		strncpy(cmd_ptr->prompt, (uint8_t *)"#", sizeof(cmd_ptr->prompt));
	    }/* else {
	        strncpy(cmd_ptr->prompt, cli_clients[current_client].cli_mode(), sizeof(cmd_ptr->prompt));
	    } */
	} else {
	    // Not already in a CLI

	    // Check for existing supported commands
	    for (i = 0; i < CLI_SYSTEM_CMDS_MAX; i++) {
	        if (strcmp_word(cmd_ptr->cmd_buffer, cmd_table[i].command) == 0) {
		    cmd_table[i].action(cmd_ptr->cmd_buffer);
		    //strncpy(cmd_ptr->prompt, "#", sizeof(cmd_ptr->prompt));
		    break;
		}
	    }

	    if (i == CLI_SYSTEM_CMDS_MAX) {
	        // Supported command not found
	       
	        // Check for CLI client
		for (i = 1; i < CLI_CLIENT_MAX; i++) {
		    if (strcmp_word(cmd_ptr->cmd_buffer, cli_clients[i].cli_name) == 0) {
		        current_client = i;
			cli_clients[current_client].cli_func(cmd_ptr->cmd_buffer, &exited);
			if (exited) {
			    current_client = CLI_CLIENT_SYSTEM;
			    //strncpy(cmd_ptr->prompt, "#", sizeof(cmd_ptr->prompt));
			    //printf("# ");
			} else {
			    strncpy(cmd_ptr->prompt, cli_clients[current_client].cli_mode(), sizeof(cmd_ptr->prompt));
			}
			break;
	            }
	        }

		if (i == CLI_CLIENT_MAX) {
		    printf("Command not found\n");
		}

	    }
       }

       if (current_client == CLI_CLIENT_SYSTEM) {
           printf("%s", cmd_ptr->prompt);
       } else {
           printf("%s>", cmd_ptr->prompt);
       }
       
       cmd_ptr->client_busy = false;

    }
}

void shell(void)
{
    err_t err_code = ERR_SUCCESS;
    cli_cmd_info_t *cmd_ptr = NULL;
    void *handle = NULL;

    err_code = open_shared_memory((uint8_t *)CLI_SHARED_MEM, &handle, (void **)&cmd_ptr);
    if (err_code != ERR_SUCCESS) {
        printf("Unable to open %s shared memory\n", CLI_SHARED_MEM);
	return;
    }

    printf("FLY: shell process started\n");

    start_shell(cmd_ptr);

}
